Skip to content

Stop false storage-degraded banners under audit read load - #63

Merged
maiphucgiang merged 7 commits into
mainfrom
fix/audit-degraded-false-alarm
Oct 15, 2026
Merged

maiphucgiang merged 7 commits into
mainfrom
fix/audit-degraded-false-alarm

Conversation

@maiphucgiang

@maiphucgiang maiphucgiang commented Oct 15, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Audit reads (ticket/dashboard/logs/storage) now run on fresh read-only WAL connections; they no longer queue on the 250ms write lock, so parallel WebUI dashboard views cannot starve live record_request commits or each other.
  • Lock-contention timeouts are reported as transient_contention (1h window + lifetime count) and never latch the 24h degraded banner; only real storage errors (sqlite/OSError) latch.
  • Dashboard P95 moves to SQL (COUNT + ORDER BY OFFSET) over a new covered (started_at, duration_ms) index; schema v4 adds the column with a one-time backfill from payloads, replacing the per-row json_extract Python fetchall.
  • /admin/storage returns the full health payload with degraded flags instead of 503, so storage-page diagnostics stay visible during degradation; page-open bounded prune slice is preserved.

Validation

  • tests/test_audit_store.py 40/40; full backend suite (per-file python -B tests/test_*.py) all exit 0; web vitest 17 files / 146 tests passed.
  • Repro at 300k rows (30d retention): before — 6–9 TimeoutError, 7/80 audit records dropped, degraded latched 24h; after — zero faults, zero drops, degraded=false.
  • Local deployment with a 300k-row seeded store: cold-start 12-way concurrent admin burst all 200 (dashboard 4.65s/503 → 0.53s/200); 12 concurrent /v1/chat/completions recorded end-to-end, no degradation. Live instance migrated v3→v4 automatically (10160/10160 rows backfilled).

Summary by Sourcery

Prevent audit read workloads from triggering false storage degradation while improving dashboard query performance and storage diagnostics.

New Features:

  • Add read-only WAL snapshots for audit queries and SQL-backed dashboard duration percentiles.
  • Expose transient lock contention separately from genuine storage failures in storage health diagnostics.
  • Display credential maintenance results and status messages within the credential detail view.
  • Add translated labels for newly surfaced audit actions and contention metrics.

Bug Fixes:

  • Prevent audit reads and dashboard bursts from starving request commits or causing dropped records and false degraded-storage banners.
  • Keep the storage health endpoint diagnostic and successful during degraded conditions.
  • Preserve storage-page cleanup while moving health scans off the write path.
  • Ensure mobile navigation can be expanded after the sidebar is collapsed.

Enhancements:

  • Migrate audit storage to schema v4 with a persisted duration column and supporting index for efficient dashboard queries.
  • Improve sidebar collapse behavior and presentation across desktop and mobile layouts.

Tests:

  • Add coverage for transient contention handling, concurrent reads and writes, dashboard load storms, and v3-to-v4 duration backfill and query planning.
  • Update end-to-end coverage for mobile sidebar interaction.

Audit reads (ticket/dashboard/logs/storage) now run on fresh read-only
WAL connections instead of queueing on the 250ms write lock, so parallel
dashboard views no longer starve live record_request commits or each
other. Lock-contention timeouts are reported as transient_contention and
never latch the 24h degraded banner; only real storage errors do.

The dashboard P95 scan moves to SQL (COUNT + ORDER BY OFFSET) over a new
covered (started_at, duration_ms) index (schema v4, backfilled once from
payloads), replacing the per-row json_extract fetchall that serialized
300k-row scans through Python. /admin/storage keeps reporting the full
health payload with degraded flags instead of 503, so the storage page
diagnostics stay visible during degradation.
@sourcery-ai

sourcery-ai Bot commented Oct 15, 2026

Copy link
Copy Markdown

Sorry @maiphucgiang, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 2 days and 23 hours by commenting @sourcery-ai review. Upgrade to get a review now.

@sourcery-ai

sourcery-ai Bot commented Oct 15, 2026

Copy link
Copy Markdown

Reviewer's Guide

The PR decouples audit reads from the write lock using read-only WAL snapshots, classifies lock contention as non-degrading transient health telemetry, accelerates dashboard P95 through a migrated indexed duration column, and keeps storage diagnostics available during degradation.

Sequence diagram for non-blocking audit reads and request recording

sequenceDiagram
    participant WebUI
    participant AuditStore
    participant ReadDB as ReadOnlyWAL
    participant WriteDB as WriteConnection
    participant SQLite

    par Dashboard read
        WebUI->>AuditStore: dashboard()
        AuditStore->>ReadDB: _open_read()
        ReadDB->>SQLite: SELECT dashboard data
        SQLite-->>ReadDB: dashboard result
        ReadDB-->>AuditStore: return result
    and Request commit
        WebUI->>AuditStore: record_request(record)
        AuditStore->>WriteDB: _run(commit, write=True)
        WriteDB->>SQLite: BEGIN IMMEDIATE and INSERT
        SQLite-->>WriteDB: commit result
        WriteDB-->>AuditStore: recorded result
    end
Loading

Entity relationship diagram for schema v4 duration indexing

erDiagram
    REQUESTS {
        text id PK
        real started_at
        real duration_ms
        text payload
    }
    REQUESTS }o--|| REQUESTS_DURATION_INDEX : covered_by
Loading

State diagram for storage health and transient contention

stateDiagram-v2
    [*] --> Healthy
    Healthy --> TransientContention: TimeoutError / _fault
    TransientContention --> Healthy: transient window expires
    Healthy --> Degraded: sqlite.Error or OSError / _fault
    TransientContention --> Degraded: real storage error
    Degraded --> Healthy: degraded window expires

    state TransientContention {
        [*] --> Reported
        Reported: transient_contention
        Reported: transient_count
    }
    state Degraded {
        [*] --> Latched
        Latched: degraded=true
        Latched: recent_failures
    }
Loading

File-Level Changes

Change Details Files
Move audit reads onto independent read-only WAL snapshots so concurrent dashboards and log/storage views no longer contend with write commits.
  • Added fresh read-only connections with bounded query progress handling.
  • Converted ticket, record listing/detail, dashboard, credential, and storage reads to the read path.
  • Kept storage-page cleanup as a bounded write slice before diagnostics are read.
app/audit_store.py
Separate expected lock contention from genuine storage failures in health reporting.
  • Track transient contention in a one-hour window and lifetime counter.
  • Prevent timeout contention from latching the 24-hour degraded state while preserving dropped-record accounting.
  • Return storage diagnostics with HTTP 200 even when degraded, including archive discovery best-effort behavior.
app/audit_store.py
app/admin_api.py
web/src/values.tsx
Optimize dashboard duration percentile queries with a persisted duration column and index.
  • Bump schema to v4 and backfill duration values from existing JSON payloads.
  • Add the covered duration index and calculate P95 with SQL COUNT/OFFSET instead of Python fetch-and-sort.
app/audit_store.py
Add regression coverage for contention classification, concurrent read/write isolation, migration, and indexed P95 behavior.
  • Verify lock timeouts remain non-degraded and are exposed as transient contention.
  • Exercise concurrent dashboard, storage, and traffic workloads without dropped records.
  • Validate v3-to-v4 backfill, schema upgrade, query-plan coverage, and P95 output.
tests/test_audit_store.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

A transient prune timeout during the concurrent mix is permitted by
design; the contract under test is zero dropped records and no degraded
latch.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New security issues found

Comment thread app/audit_store.py
Comment thread app/audit_store.py
Comment thread app/audit_store.py
Comment thread app/audit_store.py
Comment thread app/audit_store.py
Comment thread app/audit_store.py
Add zh/en labels and value mappings for storage_archive,
credentials.uploaded/exported, session.revoke_failed, and the
credential.auto_checkin/auto_travel settings events.
Replace the small header icon button with a full-width pill bar pinned
to the sidebar bottom (label plus panel icon, icon-only when
collapsed), matching the existing glass panel styling.
On narrow screens the collapsed state now hides the brand row and nav
strip entirely, leaving the labeled expand bar as the only remnant
instead of an icon-only nav. Also apply vp check formatting to the
recent sidebar and label edits.
The collapsed mobile sidebar now hides the nav strip behind the expand
bar; the test expands it before navigating and asserts the stow.
Maintenance results, preference notices, busy state and errors now
render at the top of the credential drawer while it is open, instead of
behind the overlay on the main page; the page-level copies return when
the drawer closes.
@maiphucgiang
maiphucgiang merged commit 31c49bc into main Oct 15, 2026
7 of 8 checks passed
@maiphucgiang
maiphucgiang deleted the fix/audit-degraded-false-alarm branch October 15, 2026 09:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant